//
// Created by hjie on 23-10-3.
//
#include <cstring>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <arpa/inet.h>

#pragma pack(1)

struct RTP_FIXED_HEADER
{
    // byte 0
    unsigned char csrc_len:4;   // expect 0
    unsigned char extension:1;  // expect 1
    unsigned char padding:1;    // expect 0
    unsigned char version:2;    // expect 2
    // byte 1   
    unsigned char payload:7;    // expect 1
    unsigned char marker:1;
    // byte 2, 3
    uint16_t seq_no;
    // byte 4-7
    uint32_t timestamp;
    // byte 8-11
    uint32_t ssrc;              // stream number 
};

struct MPEGTS_FIXED_HEADER
{
    unsigned sync_byte:8;
    unsigned transport_error_indicator:1;
    unsigned payload_unit_start_indicator:1;
    unsigned transport_priority:1;
    unsigned PID:13;
    unsigned scrambling_control:2;
    unsigned adaptation_field_exit:2;
    unsigned continuity_counter:4;
};

int sample_udp_parser(int port)
{
    FILE * fp1 = fopen("output_dump.tx", "wb+");
    
    int server_socket = socket(AF_INET, SOCK_DGRAM, 0);
    
    struct sockaddr_in sai;
    bzero(&sai, sizeof(sai));
    
    sai.sin_family = AF_INET;
    sai.sin_port   = htons(port);
    sai.sin_addr.s_addr = INADDR_ANY;
    
    bind(server_socket, (struct sockaddr*)&sai, sizeof(sai));
    
    struct sockaddr_in remote_sai;
    socklen_t remote_socket_length = 0;
    char buff[2000] = {0};
    
    int parse_rtp = 1;
    int parse_mpeg_ts = 1;
    int cnt = 0;
    while (1)
    {
        bzero(&buff, sizeof(buff));
        bzero(&remote_sai, sizeof(remote_sai));
        remote_socket_length = 0;
        
        int packet_size = recvfrom(server_socket, buff, 2000, 0, (struct sockaddr*)&remote_sai, &remote_socket_length);
        if (packet_size > 0)
        {
            if (parse_rtp != 0)
            {
                char payload_str[10] = {0};
                RTP_FIXED_HEADER rtp_fix_header{};
                int rtp_fix_header_size = sizeof(rtp_fix_header);

                memcpy((void*)&rtp_fix_header, buff, rtp_fix_header_size);
                
                char payload = rtp_fix_header.payload;
                switch (payload) 
                {
                    case 0:
                    case 1:
                    case 2:
                    case 3:
                    case 4:
                    case 5:
                    case 6:
                    case 7:
                    case 8:
                    case 9:
                    case 10:
                    case 11:
                    case 12:
                    case 13:
                    case 14:
                    case 15:
                    case 16:
                    case 17:
                    case 18:
                    {
                        sprintf(payload_str, "Audio");
                        break;
                    }
                    case 31:
                    {
                        sprintf(payload_str, "H.261");
                        break;
                    }
                    case 32:
                    {
                        sprintf(payload_str, "MPV");
                        break;
                    }
                    case 33:
                    {
                        sprintf(payload_str, "MP2T");
                        break;
                    }
                    case 34:
                    {
                        sprintf(payload_str, "H.263");
                        break;
                    }
                    case 96:
                    {
                        sprintf(payload_str, "H.264");
                        break;
                    }
                    default:
                        sprintf(payload_str, "other");
                        break;
                }
                unsigned int timestamp = ntohl(rtp_fix_header.timestamp);
                unsigned int seq_no    = ntohs(rtp_fix_header.seq_no);

                printf("[RTP pkt] %5d| %5s| %10u| %5d| %5d|\n", cnt, payload_str, timestamp, seq_no, packet_size);
                // RTP DATA
                char * rtp_data = buff + rtp_fix_header_size;
                int rtp_data_size = packet_size - rtp_fix_header_size;
                // parse mpeg ts
                if (parse_mpeg_ts != 0 && payload == 33)
                {
                    MPEGTS_FIXED_HEADER mpeg_ts_header{};
                    for (int index = 0; index < rtp_data_size; index+=188)
                    {
                        if (rtp_data[index] != 0x47)
                        {
                            break;
                        }
                        mpeg_ts_header.sync_byte = rtp_data[index];
                        mpeg_ts_header.transport_error_indicator = rtp_data[index + 1] & 0b10000000;
                        mpeg_ts_header.payload_unit_start_indicator = rtp_data[index + 1] & 0b01000000;
                        mpeg_ts_header.transport_priority = rtp_data[index + 1] & 0b00100000;
                        mpeg_ts_header.PID = ((rtp_data[index + 1] & 0b00011111) << 8) | (rtp_data[index + 2] & 0xff);
                        mpeg_ts_header.scrambling_control = rtp_data[index + 3] & 0b11000000;
                        mpeg_ts_header.adaptation_field_exit = rtp_data[index + 3] & 0b00110000;
                        mpeg_ts_header.continuity_counter    = rtp_data[index + 3] & 0b00001111;
                        printf("\t[MPEG Pkt PID : %d order count : %d]\n", mpeg_ts_header.PID, mpeg_ts_header.continuity_counter);
                    }
                }
            }
            else
            {
                printf("[UDP Pkt] %5d| %5d|\n", cnt, packet_size);
            }
            cnt++;
        }
    }
}

int main(int argc, const char * argv[])
{
    sample_udp_parser(9000);
    return EXIT_SUCCESS;
}